﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RuleOfReady.Core.DomainModels.Tiles;
using RuleOfReady.Core.Test.Util;

namespace RuleOfReady.Core.Test.DomainModelTest
{
    [TestClass]
    public class MahjongTileTests
    {
        [TestMethod]
        public void SuitTileEquality()
        {
            MahjongSuitTile bam4 = new MahjongSuitTile(MahjongSuitType.Bamboo, MahjongSuitNumber.Four);
            MahjongSuitTile dot5 = new MahjongSuitTile(MahjongSuitType.Dot, MahjongSuitNumber.Five);
            MahjongSuitTile anotherBam4 = new MahjongSuitTile(MahjongSuitType.Bamboo, MahjongSuitNumber.Four);
            MahjongSuitTile redDot5 = new MahjongSuitTile(MahjongSuitType.Dot, MahjongSuitNumber.Five, isRedBonus: true);

            MahjongSuitTile dot5int = new MahjongSuitTile(MahjongSuitType.Dot, 5);

            Assert.AreNotEqual(bam4, dot5);
            Assert.AreEqual(bam4, anotherBam4);
            Assert.AreEqual(bam4, bam4);
            Assert.AreEqual(dot5, redDot5);
            Assert.AreNotEqual(bam4, redDot5);
            Assert.AreEqual(dot5, dot5int);
            MahjongSuitTile bam5 = new MahjongSuitTile(MahjongSuitType.Bamboo, MahjongSuitNumber.Five);
            Assert.AreNotEqual(bam4, bam5);

            Assert.IsTrue(bam4 == anotherBam4);
            Assert.IsFalse(bam4 == redDot5);
            Assert.IsFalse(dot5 != redDot5);
        }

        [TestMethod]
        public void HonorTileEquality()
        {
            MahjongHonorTile rd = new MahjongHonorTile(MahjongHonorType.RedDragon);
            MahjongHonorTile redDragon = new MahjongHonorTile(MahjongHonorType.RedDragon);
            MahjongHonorTile eastWind = new MahjongHonorTile(MahjongHonorType.EastWind);

            Assert.AreEqual(rd, redDragon);
            Assert.AreNotEqual(redDragon, eastWind);

            Assert.IsTrue(rd == redDragon);
            Assert.IsFalse(rd == eastWind);
        }

        [TestMethod]
        public void TileEquality()
        {
            MahjongTile dot5 = new MahjongSuitTile(MahjongSuitType.Dot, MahjongSuitNumber.Five);
            MahjongTile redDot5 = new MahjongSuitTile(MahjongSuitType.Dot, MahjongSuitNumber.Five, isRedBonus: true);

            Assert.AreEqual(dot5, redDot5);

            Assert.IsTrue(dot5 == redDot5);
        }

        [TestMethod]
        public void HonorTileCompare()
        {
            MahjongHonorTile whiteDragon = new MahjongHonorTile(MahjongHonorType.WhiteDragon);
            MahjongHonorTile westWind = new MahjongHonorTile(MahjongHonorType.WestWind);
            MahjongHonorTile anotherWhiteDragon = new MahjongHonorTile(MahjongHonorType.WhiteDragon);

            Assert.IsTrue(whiteDragon.CompareTo(westWind) > 0);
            Assert.IsTrue(whiteDragon.CompareTo(anotherWhiteDragon) == 0);

            Assert.IsTrue(whiteDragon > westWind);
            Assert.IsTrue(whiteDragon <= anotherWhiteDragon && whiteDragon >= anotherWhiteDragon);
        }

        [TestMethod]
        public void SuitTileCompare()
        {
            MahjongSuitTile char3 = new MahjongSuitTile(MahjongSuitType.Character, MahjongSuitNumber.Three);
            MahjongSuitTile char5 = new MahjongSuitTile(MahjongSuitType.Character, MahjongSuitNumber.Five);
            MahjongSuitTile bam1 = new MahjongSuitTile(MahjongSuitType.Bamboo, MahjongSuitNumber.One);
            MahjongSuitTile dot1 = new MahjongSuitTile(MahjongSuitType.Dot, MahjongSuitNumber.One);
            MahjongSuitTile anotherDot1 = new MahjongSuitTile(MahjongSuitType.Dot, MahjongSuitNumber.One);


            Assert.IsTrue(char3.CompareTo(char5) < 0);
            Assert.IsTrue(bam1.CompareTo(dot1) < 0);
            Assert.IsTrue(dot1.CompareTo(char3) > 0);
            Assert.IsTrue(dot1.CompareTo(anotherDot1) == 0);

            Assert.IsTrue(char3 < char5);
            Assert.IsTrue(bam1 < dot1);
            Assert.IsTrue(dot1 > char3);
            Assert.IsTrue(dot1 <= anotherDot1 && dot1 >= anotherDot1);
        }

        [TestMethod]
        public void TileCompare()
        {
            MahjongTile eastWind = new MahjongHonorTile(MahjongHonorType.EastWind);
            MahjongTile dot9 = new MahjongSuitTile(MahjongSuitType.Dot, MahjongSuitNumber.Nine);

            MahjongTile anotherEastWind = new MahjongHonorTile(MahjongHonorType.EastWind);
            MahjongTile anotherDot9 = new MahjongSuitTile(MahjongSuitType.Dot, MahjongSuitNumber.Nine);

            Assert.IsTrue(eastWind.CompareTo(dot9) > 0);
            Assert.IsTrue(eastWind.CompareTo(anotherEastWind) == 0);
            Assert.IsTrue(dot9.CompareTo(anotherDot9) == 0);

            Assert.IsTrue(eastWind > dot9);
            Assert.IsTrue(eastWind <= anotherEastWind && eastWind >= anotherEastWind);
            Assert.IsTrue(dot9 <= anotherDot9 && dot9 >= anotherDot9);


        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void ExceptionForBadSuitValue()
        {
            try
            {
                MahjongTile badTile = new MahjongSuitTile(MahjongSuitType.Bamboo, 20);
            }
            catch (ArgumentException ex)
            {
                Assert.IsTrue(ex.ParamName == "suitNumber");
                throw;
            }
        }

        [TestMethod]
        public void TileHashCodesUnique()
        {
            Dictionary<int, MahjongTile> hashcodeDictionary = new Dictionary<int, MahjongTile>();

            foreach (MahjongHonorType honorType in Enum.GetValues(typeof(MahjongHonorType)))
            {
                MahjongHonorTile tile = new MahjongHonorTile(honorType);
                int hashCode = tile.GetHashCode();
                Assert.IsFalse(hashcodeDictionary.ContainsKey(hashCode), "tile hashcode collision");
                hashcodeDictionary[hashCode] = tile;
            }

            foreach (MahjongSuitType suitType in Enum.GetValues(typeof(MahjongSuitType)))
            {
                foreach (MahjongSuitNumber suitNumber in Enum.GetValues(typeof(MahjongSuitNumber)))
                {
                    MahjongSuitTile tile = new MahjongSuitTile(suitType, suitNumber);
                    int hashCode = tile.GetHashCode();
                    Assert.IsFalse(hashcodeDictionary.ContainsKey(hashCode), "tile hashcode collision");
                    hashcodeDictionary[hashCode] = tile;
                }
            }

            for (int i = 1; i < 10; i++)
            {
                foreach (MahjongHonorType honorType in Enum.GetValues(typeof(MahjongHonorType)))
                {
                    MahjongHonorTile tile = new MahjongHonorTile(honorType);
                    int hashCode = tile.GetHashCode();
                    bool match = (hashcodeDictionary.ContainsKey(hashCode) && hashcodeDictionary[hashCode] == tile);
                    Assert.IsTrue(match, "honor tile hashcode not consistent");
                }


                foreach (MahjongSuitType suitType in Enum.GetValues(typeof(MahjongSuitType)))
                {
                    foreach (MahjongSuitNumber suitNumber in Enum.GetValues(typeof(MahjongSuitNumber)))
                    {
                        MahjongSuitTile tile = new MahjongSuitTile(suitType, suitNumber);
                        int hashCode = tile.GetHashCode();
                        bool match = (hashcodeDictionary.ContainsKey(hashCode) && hashcodeDictionary[hashCode] == tile);
                        Assert.IsTrue(match, "suit tile hashcode not consistent");
                    }
                }
            }
        }

    }
}
